﻿namespace EFS.RC.CC.Applications.EndPoints
{
	/// <summary>
	/// Application end point
	/// </summary>
	internal abstract class EndPoint<EndPointDeployment> : EFS.Common.Async.AsyncRoot
		where EndPointDeployment : EFS.RC.CC.Deployment.Entities.EndPoint
	{
		#region Components

		// sync access
		private object syncAccess = new object();

		// Collaboration end point
		private Microsoft.Rtc.Collaboration.ApplicationEndpoint collaborationApplicationEndpoint = null;

		/// <summary>
		/// Status
		/// </summary>
		public enum EndPointStatus { Idle, Started, Terminated }

		// Status
		private EndPointStatus status = EndPointStatus.Idle;

		#endregion

		#region Delegates

		/// <summary>
		/// Asynk terminate 
		/// </summary>
		public EFS.Common.Async.AsyncDelegate<EFS.RC.CC.Applications.EndPoints.EndPoint<EndPointDeployment>> AsinkTerminatedDelegate { get; private set; }

		#endregion

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		protected EndPoint(Microsoft.Rtc.Collaboration.ApplicationEndpointSettings applicationEndpointSettings)
		{
			// check
			EFS.Common.Verify.Reference(applicationEndpointSettings, "applicationEndpointSettings");

			// delegate
			AsinkTerminatedDelegate = new EFS.Common.Async.AsyncDelegate<EndPoint<EndPointDeployment>>(this);

			// make end oint
			collaborationApplicationEndpoint = new Microsoft.Rtc.Collaboration.ApplicationEndpoint(EFS.RC.CC.Platform.Instance.CollaborationPlatform, applicationEndpointSettings);
		}

		#endregion

		#region Usage

		/// <summary>
		/// Describe this object
		/// </summary>
		/// <returns></returns>
		public override System.String Describe()
		{
			return System.String.Format("EFS.RC.CC.Applications.EndPoints.EndPoint [{0}]", collaborationApplicationEndpoint.EndpointUri);
		}

		/// <summary>
		/// Begin startup
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginStartup()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginStartup", this);

			// start
			return BeginAsyncProcess2(Startup, OnStartupComplete);
		}

		/// <summary>
		/// Begin startup
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginShutdown()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginStartup", this);

			// stop end point
			return BeginAsyncProcess2(Shutdown, OnShutdownComplete);
		}

		/// <summary>
		/// Get Collaboration ApplicationEndpoint
		/// </summary>
		internal Microsoft.Rtc.Collaboration.ApplicationEndpoint CollaborationApplicationEndpoint
		{
			get { return collaborationApplicationEndpoint; }
		}

		/// <summary>
		/// Get status
		/// </summary>
		public EndPointStatus Status { get { return status; } }
		
		#endregion

		#region Startup

		/// <summary>
		/// Platform startup
		/// </summary>
		private void Startup()
		{
			// log
			EFS.EventLog.Log.LogInfo("Startup", this);

			// lock access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				if (status != EndPointStatus.Idle)
				{
					throw new System.InvalidOperationException("Only Idle EndPoint can be started");
				}

				// started
				status = EndPointStatus.Started;
			}

			// new app end point
			collaborationApplicationEndpoint.StateChanged += OnStateChanged;

			// sstablish
			collaborationApplicationEndpoint.BeginEstablish(
				EFS.Common.Async.AsyncResult.StateDelegate.End,
				EFS.Common.Async.AsyncResult.StateDelegate.State(OnEstablishComplete));
		}

		/// <summary>
		/// End point established
		/// </summary>
		/// <param name="result"></param>
		private void OnEstablishComplete(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnEstablishComplete", this);

			try
			{
				// make sure all ok
				collaborationApplicationEndpoint.EndEstablish(ar);

				// log
				EFS.EventLog.Log.LogInfo("OnEstablishComplete SUCCCESS", this);

				// call active
				PostEndPointEstablish(collaborationApplicationEndpoint);
			}
			catch (System.Exception ex)
			{
				// handle exception
				HandleExceptionSafe(ex);
			}
		}

		/// <summary>
		/// Start-up complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnStartupComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnStartupComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				EFS.EventLog.Log.LogInfo("OnStartupComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// handle exception
				HandleExceptionSafe(ex);
			}
		}

		/// <summary>
		/// On Active
		/// </summary>
		protected abstract void PostEndPointEstablish(Microsoft.Rtc.Collaboration.ApplicationEndpoint collaborationApplicationEndpoint);

		#endregion Startup

		#region Runtime

		/// <summary>
		/// Unhandled exception
		/// </summary>
		/// <param name="ex"></param>
		protected override void OnException(System.Exception ex)
		{
			// check
			EFS.Common.Verify.Reference(ex, "ex");

			// SAL
			EFS.RC.CC.Tools.Loggers.SAL.Add(null, "OnException", ex, this); 

			// log
			EFS.EventLog.Log.LogException(ex, this);

			// shut down
			BeginShutdown();
		}

		/// <summary>
		/// State changed evensnts
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private void OnStateChanged(object sender, Microsoft.Rtc.Collaboration.LocalEndpointStateChangedEventArgs args)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnStateChanged", this);

			try
			{
				// check for removed end point and get it down
				if (args.Reason == Microsoft.Rtc.Collaboration.LocalEndpointStateTransitionReason.OwnerDisabledOrRemoved)
				{
					// get down
					BeginShutdown();
				}
			}
			catch (System.Exception ex)
			{
				// handle ex
				HandleExceptionSafe(ex);
			}
		}

		#endregion Runtime
	
		#region Shutdown

		/// <summary>
		/// Pre shut down
		/// </summary>
		protected abstract void PreEndPointTerminate(Microsoft.Rtc.Collaboration.ApplicationEndpoint collaborationApplicationEndpoint);

		/// <summary>
		/// Sht down
		/// </summary>
		private void Shutdown()
		{
			// log
			EFS.EventLog.Log.LogInfo("Shutdown", this);

			// lock local acess
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check already terminated
				if (status != EndPointStatus.Terminated)
				{
					// set terinated
					status = EndPointStatus.Terminated;

					// notify terminate
					AsinkTerminatedDelegate.Start();
				}
				else
				{
					// already terminated
					return;
				}
			}

			// check
			if (null != collaborationApplicationEndpoint)
			{
				// remove collaboration delegates
				collaborationApplicationEndpoint.StateChanged -= OnStateChanged;

				// call shut down delegates
				PreEndPointTerminate(collaborationApplicationEndpoint);

				// check
				if (collaborationApplicationEndpoint.State == Microsoft.Rtc.Collaboration.LocalEndpointState.Established)
				{
					// drain app
					collaborationApplicationEndpoint.BeginDrain(
						EFS.Common.Async.AsyncResult.StateDelegate.End,
						EFS.Common.Async.AsyncResult.StateDelegate.State(EndPointDrainComplete));
				}
				else
				{
					// terminate app
					collaborationApplicationEndpoint.BeginTerminate(
						EFS.Common.Async.AsyncResult.StateDelegate.End,
						EFS.Common.Async.AsyncResult.StateDelegate.State(EndPointTerminateComplete));
				}
			}
		}

		/// <summary>
		/// End point drain
		/// </summary>
		/// <param name="result"></param>
		private void EndPointDrainComplete(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("EndPointTerminateComplete", this);

			try
			{
				// make sure all ok
				collaborationApplicationEndpoint.EndDrain(ar);

				// terminate app
				collaborationApplicationEndpoint.BeginTerminate(
					EFS.Common.Async.AsyncResult.StateDelegate.End,
					EFS.Common.Async.AsyncResult.StateDelegate.State(EndPointTerminateComplete));

				// log
				EFS.EventLog.Log.LogInfo("EndPointTerminateComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
			finally
			{
				// call stopped 
				EndPointStopped();
			}
		}

		/// <summary>
		/// End point established
		/// </summary>
		/// <param name="result"></param>
		private void EndPointTerminateComplete(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("EndPointTerminateComplete", this);

			try
			{
				// make sure all ok
				collaborationApplicationEndpoint.EndTerminate(ar);

				// log
				EFS.EventLog.Log.LogInfo("EndPointTerminateComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
			finally
			{
				try
				{
					// call stopped 
					EndPointStopped();
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);
				}
			}
		}

		/// <summary>
		/// Stopped
		/// </summary>
		protected abstract void EndPointStopped();

		/// <summary>
		/// Shut down complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnShutdownComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnShutdownComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				EFS.EventLog.Log.LogInfo("OnShutdownComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		#endregion // shutdown
	}
}
